package com.xjnt.frameworks.core;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Page;
import com.xjnt.frameworks.plugin.sql.criterion.Restrictions;
import com.xjnt.frameworks.plugin.sql.criterion.SqlCriteria;
import com.xjnt.frameworks.plugin.sql.criterion.TypedValue;
import com.xjnt.frameworks.plugin.sql.criterion.expression.Order;
import com.xjnt.frameworks.web.constants.SearchType;

public abstract class BaseService<M extends BaseModel<M>> {

	public abstract M getModel();
	
	public void clearCache(){
		
	}

	public boolean save(M model) {
		clearCache();
		return model.save();
	}

	public boolean deleteById(Object id) {
		clearCache();
		return getModel().deleteById(id);
	}

	public boolean deleteByIds(Object[] ids) {
		for (Object id : ids) {
			deleteById(id);
		}
		return true;
	}

	public boolean delete(M model) {
		clearCache();
		return model.delete();
	}

	public boolean deleteAll(List<Object> ids) {
		for (Object id : ids) {
			deleteById(id);
		}
		return true;
	}

	public boolean update(M model) {
		clearCache();
		return model.update();
	}

	public boolean update(List<M> models) {
		for (M model : models) {
			update(model);
		}
		return true;
	}

	public M findById(Object id) {
		return getModel().findById(id);
	}

	public List<M> findAll() {
		SqlCriteria criteria = new SqlCriteria(getModel());
		String sql = criteria.toSelectSql() + criteria.toConditionSql();
		return getModel().find(sql);
	}

	public Page<M> paginate(int pageNumber, int pageSize, Map<String, String[]> conditions, SearchType searchType) {
		SqlCriteria criteria = getCriteria(conditions, searchType);
		Object[] paras = null;
		String select = criteria.toSelectSql();
		String from = criteria.toConditionSql();
		List<TypedValue> typedValues = criteria.getTypedValues();
		if (null != typedValues && !typedValues.isEmpty()) {
			paras = new Object[typedValues.size()];
			for (int i = 0; i < typedValues.size(); i++) {
				paras[i] = typedValues.get(i).getValue();
			}
		}
		if (paras != null) {
			System.out.println(paras.length);
			return getModel().paginate(pageNumber, pageSize, select, from, paras);
		} else {
			return getModel().paginate(pageNumber, pageSize, select, from);
		}
	}
	
	protected SqlCriteria getCriteria(Map<String, String[]> conditions, SearchType searchType){
		SqlCriteria criteria = new SqlCriteria(getModel());
		if (conditions != null) {
			Map<String, Class<?>> columnMap = getModel().getTable().getColumnTypeMap();
			Set<Entry<String, String[]>> setCondition = conditions.entrySet();
			Iterator<Entry<String, String[]>> itCondition = setCondition.iterator();
			while (itCondition.hasNext()) {
				Entry<String, String[]> entry = itCondition.next();
				String conditionName = entry.getKey();
				String[] conditionVal = entry.getValue();
				String[] tmpNames = conditionName.split("\\.");
				String colName = conditionName;
				if(tmpNames.length > 1){
					colName = tmpNames[1];
				}
				boolean hasColumn = getModel().getTable().hasColumnLabel(colName);
				if(hasColumn){
					Class<?> colType = columnMap.get(colName);
					combine(criteria, criteria.getAlias() + "." + colName, conditionVal, colType, searchType);
				}
			}
			Object[] sortName = conditions.get("sortName");
			Object[] sortOrder = conditions.get("sortOrder");
			orderCondition(criteria, sortName, sortOrder);
		}
		return criteria;
	}

	protected void combine(SqlCriteria criteria, String colName, String[] colValue, Class<?> colType,
			SearchType searchType) {
		if (colType == java.sql.Timestamp.class) {
			timeStampCondition(criteria, colName, colValue);
		} else if (colType == java.sql.Date.class) {
			timeStampCondition(criteria, colName, colValue);
		} else if (colType == java.util.Date.class) {
			timeStampCondition(criteria, colName, colValue);
		} else if (colType == java.lang.Integer.class || 
				   colType == java.lang.Long.class || 
				   colType == java.lang.Double.class) {
			numberCondition(criteria, colName, colValue);
		} else {
			stringCondition(criteria, colName, colValue, searchType);
		}
	}

	protected void stringCondition(SqlCriteria criteria, String colName, String[] colValue, SearchType searchType) {
		if (StrKit.notBlank(colValue[0])) {
			switch (searchType) {
			case General:
				criteria.add(Restrictions.like(colName, colValue[0]));
				break;
			case Advanced:
				criteria.add(Restrictions.eq(colName, colValue[0]));
				break;
			}
		}
	}
	
	protected void numberCondition(SqlCriteria criteria, String colName, String[] colValue) {
		if (StrKit.notBlank(colValue[0])) {
			criteria.add(Restrictions.eq(colName, colValue[0]));
		}
	}

	protected void timeStampCondition(SqlCriteria criteria, String colName, String[] colValue) {
		if (StrKit.notBlank(colValue)) {
			criteria.add(Restrictions.between(colName, colValue[0], colValue[1]));
		} else if (StrKit.notBlank(colValue[0])) {
			criteria.add(Restrictions.ge(colName, colValue[0]));
		} else {
			criteria.add(Restrictions.le(colName, colValue[1]));
		}
	}
	
	protected void orderCondition(SqlCriteria criteria, Object[] sortName, Object[] sortOrder) {
		String orderName = null;
		String orderSort = null;
		if(sortName != null && sortName.length > 0){
			orderName = sortName[0].toString();
		}
		if(sortOrder != null && sortOrder.length > 0){
			orderSort = sortOrder[0].toString();
		}
		if(StrKit.notBlank(orderName, orderSort)){
			boolean sort = orderSort.equals("asc") ? true : false;
			if(sort){
				criteria.addOrder(Order.asc(criteria.getAlias() + "." + orderName));
			} else {
				criteria.addOrder(Order.desc(criteria.getAlias() + "." + orderName));
			}
		}
	}
}
